﻿using System.Linq;
using System;
using System.Collections.Generic;
using System.Windows.Controls;
using Microsoft.Popfly.GameCreator.GameEngine.Collisions;
using Microsoft.Popfly.GameCreator.GameEngine.Events;
using System.Windows;
using System.Windows.Media;

namespace Microsoft.Popfly.GameCreator.GameEngine
{
    public class Scene : BehaviorOwner, IUpdateable
    {
        public enum SceneType { NORMAL, INTRO, WON, LOST, HOWTOPLAY }
        public SceneType Type;

        // Since this is set-able, we might want to make sure we do something with it when set
        public override double Width { get; set; }

        public override double Height { get; set; }

        public bool UsingViewport { get; set; }

        private double m_x, m_y;
        public override double X 
        { 
            get 
            { 
                return m_x; 
            }
            set
            {
                if (value < 0)
                {
                    m_x = 0;
                }
                else if (value > Width - ViewportWidth)
                {
                    m_x = Width - ViewportWidth;
                }
                else
                {
                    m_x = value;
                }
            }
        }
        public override double Y 
        {
            get
            {
                return m_y;
            }
            set
            {
                if (value < 0)
                {
                    m_y = 0; 
                }
                else if (value > Height - ViewportHeight)
                {
                    m_y = Height - ViewportHeight;
                }
                else
                {
                    m_y = value;
                }
            }
        }

        private double PrevViewportX;
        public double ViewportX { get { return X; } set { X = value; } }

        private double PrevViewportY;
        public double ViewportY { get { return Y; } set { Y = value; } }

        public double ViewportWidth { get; set; }

        public double ViewportHeight { get; set; }

        public string MusicUrl { get; private set; }

        private Dictionary<string, Actor> m_actorsDictionary = new Dictionary<string, Actor> ();
        private List<Actor> m_actorsList = new List<Actor>();

        private List<Actor> m_newMotionsFromCollisions = new List<Actor>();

        private Dictionary<string, Data.ActorInfo> m_actorData;
        private Data.SceneInfo m_sceneInfo;

        private Canvas m_sceneCanvas;
        private Canvas m_backgroundCanvas;


        // Collision event listener stuff
        // These Dictionaries are references to actors or actor instances and lists of which other actors 
        // collision events are listening for collisions with
        private Dictionary<string, List<CollisionClassWithListener>> m_actorListenersOtherClass = new Dictionary<string, List<CollisionClassWithListener>>();
        private Dictionary<string, List<CollisionClassWithListener>> m_actorInstanceListenersOtherClass = new Dictionary<string, List<CollisionClassWithListener>>();
        private List<CollisionClassWithListener> m_solidListenersOtherClass = new List<Events.CollisionClassWithListener>();
        private List<CollisionClassWithListener> m_nonSolidListenersOtherClass = new List<Events.CollisionClassWithListener>();
        private List<CollisionClassWithListener> m_allListenersOtherClass = new List<Events.CollisionClassWithListener>();

        private bool m_queueCollisionListenerRemoves;
        private Dictionary<string, CollisionClassWithListener> m_actorCollisionRemoveQueue = new Dictionary<string, CollisionClassWithListener>();
        private Dictionary<string, CollisionClassWithListener> m_actorInstanceCollisionRemoveQueue = new Dictionary<string, CollisionClassWithListener>();
        private List<CollisionClassWithListener> m_solidCollisionRemoveQueue = new List<CollisionClassWithListener>();
        private List<CollisionClassWithListener> m_nonSolidCollisionRemoveQueue = new List<CollisionClassWithListener>();
        private List<CollisionClassWithListener> m_allCollisionRemoveQueue = new List<CollisionClassWithListener>();

        private List<Actor> m_actorRemoveQueue = new List<Actor>();

        private void AddActorCollisionListener(
            string actorName, 
            CollisionClassWithListener otherActorCollisionClass)
        {
            if (!m_actorListenersOtherClass.ContainsKey(actorName))
            {
                m_actorListenersOtherClass.Add(actorName, new List<CollisionClassWithListener>());
            }
            m_actorListenersOtherClass[actorName].Add(otherActorCollisionClass);
        }
        private bool RemoveActorCollisionListener(
            string actorName,
            CollisionClassWithListener otherActorCollisionClass)
        {
            if (m_queueCollisionListenerRemoves)
            {
                otherActorCollisionClass.Owner.QueuedForRemoval = true;
                m_actorCollisionRemoveQueue.Add(actorName, otherActorCollisionClass);
                return true;
            }
            else
            {
                return m_actorListenersOtherClass[actorName].Remove(otherActorCollisionClass);
            }
        }
        private void AddActorInstanceCollisionListener(
            string actorInstanceName,
            CollisionClassWithListener otherActorCollisionClass)
        {
            if (!m_actorInstanceListenersOtherClass.ContainsKey(actorInstanceName))
            {
                m_actorInstanceListenersOtherClass.Add(actorInstanceName, new List<CollisionClassWithListener>());
            }
            m_actorInstanceListenersOtherClass[actorInstanceName].Add(otherActorCollisionClass);
        }
        private bool RemoveActorInstanceCollisionListener(
            string actorInstanceName,
            CollisionClassWithListener otherActorCollisionClass)
        {
            if (m_queueCollisionListenerRemoves)
            {
                otherActorCollisionClass.Owner.QueuedForRemoval = true;
                m_actorInstanceCollisionRemoveQueue.Add(actorInstanceName, otherActorCollisionClass);
                return true;
            }
            else 
            {
                return m_actorInstanceListenersOtherClass[actorInstanceName].Remove(otherActorCollisionClass);
            }
        }
        private void AddSolidCollisionListener(
            CollisionClassWithListener otherActorCollisionClass)
        {
            m_solidListenersOtherClass.Add(otherActorCollisionClass);
        }
        private bool RemoveSolidCollisionListener(
            CollisionClassWithListener otherActorCollisionClass)
        {
            if (m_queueCollisionListenerRemoves)
            {
                otherActorCollisionClass.Owner.QueuedForRemoval = true;
                m_solidCollisionRemoveQueue.Add(otherActorCollisionClass);
                return true;
            }
            else 
            {
                return m_solidListenersOtherClass.Remove(otherActorCollisionClass);
            }
        }
        internal void AddNonSolidCollisionListener(
           CollisionClassWithListener otherActorCollisionClass)
        {
            m_nonSolidListenersOtherClass.Add(otherActorCollisionClass);
        }
        private bool RemoveNonSolidCollisionListener(
            CollisionClassWithListener otherActorCollisionClass)
        {
            if (m_queueCollisionListenerRemoves)
            {
                otherActorCollisionClass.Owner.QueuedForRemoval = true;
                m_nonSolidCollisionRemoveQueue.Add(otherActorCollisionClass);
                return true;
            }
            else 
            {
                return m_nonSolidListenersOtherClass.Remove(otherActorCollisionClass);
            }
        }

        internal void AddAllCollisionListener(
            CollisionClassWithListener otherActorCollisionClass)
        {
            m_allListenersOtherClass.Add(otherActorCollisionClass);
        }
        private bool RemoveAllCollisionListener(
            CollisionClassWithListener otherActorCollisionClass)
        {
            if (m_queueCollisionListenerRemoves)
            {
                otherActorCollisionClass.Owner.QueuedForRemoval = true;
                m_allCollisionRemoveQueue.Add(otherActorCollisionClass);
                return true;
            }
            else
            {
                return m_allListenersOtherClass.Remove(otherActorCollisionClass);
            }
        }

        private void AddCollisionListenerHelper(CollisionClassWithListener actorCollisionClass, CollisionClassWithListener otherActorCollisionClass)
        {
            switch (actorCollisionClass.Type)
            {
                case CollisionClass.SubType.ACTOR :
                    AddActorCollisionListener(actorCollisionClass.ActorOrInstance, otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.ACTORINSTANCE :
                    AddActorInstanceCollisionListener(actorCollisionClass.ActorOrInstance, otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.NONSOLID :
                    AddNonSolidCollisionListener(otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.SOLID :
                    AddSolidCollisionListener(otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.ALL :
                    AddAllCollisionListener(otherActorCollisionClass);
                    break;
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to add a collision listener with a collision class of '" +
                        actorCollisionClass.Type.ToString() +
                        "'.");
            }
        }
        internal void AddCollisionListener(CollisionListener collisionListener)
        {
            AddCollisionListenerHelper(collisionListener.CollisionClasses[0],
                                       collisionListener.CollisionClasses[1]);
            AddCollisionListenerHelper(collisionListener.CollisionClasses[1],
                                       collisionListener.CollisionClasses[0]);
        }

        private bool RemoveCollisionListenerHelper(CollisionClassWithListener collisionClass, 
                                                   CollisionClassWithListener otherCollisionClass)
        {
            switch (collisionClass.Type)
            {
                case CollisionClass.SubType.ACTOR:
                    return RemoveActorCollisionListener(collisionClass.ActorOrInstance, otherCollisionClass);
                case CollisionClass.SubType.ACTORINSTANCE:
                    return RemoveActorInstanceCollisionListener(collisionClass.ActorOrInstance, otherCollisionClass);
                case CollisionClass.SubType.NONSOLID:
                    return RemoveNonSolidCollisionListener(otherCollisionClass);
                case CollisionClass.SubType.SOLID:
                    return RemoveSolidCollisionListener(otherCollisionClass);
                case CollisionClass.SubType.ALL:
                    return RemoveAllCollisionListener(otherCollisionClass);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to remove a collision listener with a collision class of '" +
                        collisionClass.Type.ToString() +
                        "'.");
            }
        }
        internal bool RemoveCollisionListener(CollisionListener collisionListener)
        {
            bool retVal = false;
            
            retVal |= RemoveCollisionListenerHelper(collisionListener.CollisionClasses[0], collisionListener.CollisionClasses[1]);
            retVal |= RemoveCollisionListenerHelper(collisionListener.CollisionClasses[1], collisionListener.CollisionClasses[0]);
            
            return retVal;
        }


        private Dictionary<string, List<CollisionClassWithEndChecker>> m_endActorListenersOtherClass =
            new Dictionary<string, List<CollisionClassWithEndChecker>>();
        private Dictionary<string, List<CollisionClassWithEndChecker>> m_endActorInstanceListenersOtherClass =
            new Dictionary<string, List<CollisionClassWithEndChecker>>();
        private List<CollisionClassWithEndChecker> m_endSolidListenersOtherClass =
            new List<Events.CollisionClassWithEndChecker>();
        private List<CollisionClassWithEndChecker> m_endNonSolidListenersOtherClass =
            new List<Events.CollisionClassWithEndChecker>();
        private List<CollisionClassWithEndChecker> m_endAllListenersOtherClass =
            new List<Events.CollisionClassWithEndChecker>();

        private void AddCollisionEndListenerHelper(
            CollisionClassWithEndChecker actorCollisionClass,
            CollisionClassWithEndChecker otherActorCollisionClass)
        {
            switch (actorCollisionClass.Type)
            {
                case CollisionClass.SubType.ACTOR:
                    if (!m_endActorListenersOtherClass.ContainsKey(actorCollisionClass.ActorOrInstance))
                    {
                        m_endActorListenersOtherClass.Add(actorCollisionClass.ActorOrInstance, new List<CollisionClassWithEndChecker>());
                    }
                    m_endActorListenersOtherClass[actorCollisionClass.ActorOrInstance].Add(otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.ACTORINSTANCE:
                    if (!m_endActorInstanceListenersOtherClass.ContainsKey(actorCollisionClass.ActorOrInstance))
                    {
                        m_endActorInstanceListenersOtherClass.Add(actorCollisionClass.ActorOrInstance, new List<CollisionClassWithEndChecker>());
                    }
                    m_endActorInstanceListenersOtherClass[actorCollisionClass.ActorOrInstance].Add(otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.NONSOLID:
                    m_endNonSolidListenersOtherClass.Add(otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.SOLID:
                    m_endNonSolidListenersOtherClass.Add(otherActorCollisionClass);
                    break;
                case CollisionClass.SubType.ALL:
                    m_endAllListenersOtherClass.Add(otherActorCollisionClass);
                    break;
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to add a collision listener with a collision class of '" +
                        actorCollisionClass.Type.ToString() +
                        "'.");
            }
        }
        internal void AddCollisionEndListener(CollisionEndChecker endChecker)
        {
            AddCollisionEndListenerHelper(endChecker.CollisionClasses[0],
                                          endChecker.CollisionClasses[1]);
            AddCollisionEndListenerHelper(endChecker.CollisionClasses[1],
                                          endChecker.CollisionClasses[0]);
        }

        private bool RemoveCollisionEndListenerHelper(CollisionClassWithEndChecker collisionClass,
                                                      CollisionClassWithEndChecker otherCollisionClass)
        {
            switch (collisionClass.Type)
            {
                case CollisionClass.SubType.ACTOR:
                    return m_endActorListenersOtherClass[collisionClass.ActorOrInstance].Remove(otherCollisionClass);
                case CollisionClass.SubType.ACTORINSTANCE:
                    return m_endActorInstanceListenersOtherClass[collisionClass.ActorOrInstance].Remove(otherCollisionClass);
                case CollisionClass.SubType.NONSOLID:
                    return m_endNonSolidListenersOtherClass.Remove(otherCollisionClass);
                case CollisionClass.SubType.SOLID:
                    return m_endSolidListenersOtherClass.Remove(otherCollisionClass);
                case CollisionClass.SubType.ALL:
                    return m_endAllListenersOtherClass.Remove(otherCollisionClass);
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to remove a collision listener with a collision class of '" +
                        collisionClass.Type.ToString() +
                        "'.");
            }
        }
        internal bool RemoveCollisionEndListener(CollisionEndChecker endChecker)
        {
            bool retVal = false;
            
            retVal |= RemoveCollisionEndListenerHelper(endChecker.CollisionClasses[0], endChecker.CollisionClasses[1]);
            retVal |= RemoveCollisionEndListenerHelper(endChecker.CollisionClasses[1], endChecker.CollisionClasses[0]);
            
            return retVal;
        }

        public void Update(double elapsedTimeInSeconds)
        {
            BehaviorsUpdate(elapsedTimeInSeconds);

            int count = m_actorsList.Count;
            for (int i = 0; i < count; i++)
            {
                Actor actor = m_actorsList[i];
                actor.Update(elapsedTimeInSeconds);
            }

            

            foreach (Actor actor in m_actorsList)
            {
                actor.PhysicsUpdate(elapsedTimeInSeconds);
            }

            DoAllPropertyChangeEventUpdates(elapsedTimeInSeconds);

            ResolveCollisions();

            Draw();
            PhysicsUpdate(elapsedTimeInSeconds); // for whatever reason, this is where the scene physics update happened in the 
                                                 // javascript engine.  Sticking it here for compat.
            foreach (Actor actor in m_actorsList)
            {
                actor.Draw();
            }

            CleanupActors();
        }

        private void PhysicsUpdate(double elapsedTimeInSeconds)
        {
            double xDrag = XDrag;
            double yDrag = YDrag;
            double xVelocity = XVelocity;
            double yVelocity = YVelocity;
            double xAcceleration = XAcceleration;
            double yAcceleration = YAcceleration;

            double updatedXVelocity = xVelocity;
            if (xAcceleration != 0 || (xDrag != 0 && xVelocity != 0))
            {
                updatedXVelocity = PhysicsUtils.GetNewVelocity(elapsedTimeInSeconds, xDrag, xVelocity, xAcceleration);
                XVelocity = updatedXVelocity;
            }

            double updatedYVelocity = yVelocity;
            if (yAcceleration != 0 || (yDrag != 0 && yVelocity != 0))
            {
                updatedYVelocity = PhysicsUtils.GetNewVelocity(elapsedTimeInSeconds, yDrag, yVelocity, yAcceleration);
                YVelocity = updatedYVelocity;
            }

            // most of these params are unnecessary unless we want to do the integral.
            if (updatedXVelocity != 0.0)
            {
                X = PhysicsUtils.GetNewPosition(elapsedTimeInSeconds, xDrag, X, xAcceleration, updatedXVelocity, xVelocity);
            }
            if (updatedYVelocity != 0.0)
            {
                Y = PhysicsUtils.GetNewPosition(elapsedTimeInSeconds, yDrag, Y, yAcceleration, updatedYVelocity, yVelocity);
            }

            // Zero out acceleration so all forces actually have to act every frame
            XAcceleration = 0;
            YAcceleration = 0;
        }

        private void Draw()
        {
            SilverlightUpdate();
        }

        private void SilverlightUpdate()
        {
            if (ViewportX != PrevViewportX)
            {
                Canvas.SetLeft(m_sceneCanvas, -ViewportX);
                PrevViewportX = ViewportX;
            }
            if (ViewportY != PrevViewportY)
            {
                Canvas.SetTop(m_sceneCanvas, -ViewportY);
                PrevViewportY = ViewportY;
            }
        }

        internal void ClearAllMouseArgsForNextUpdate()
        {
            ClearMouseArgsForNextUpdate();
            foreach (Actor actor in m_actorsList)
            {
                actor.ClearMouseArgsForNextUpdate();
            }
        }

        private void DoAllPropertyChangeEventUpdates(double elapsedTimeInSeconds)
        {
            PrepareAllForChangeEvents();
            PropertyChangeEventBehaviorsUpdate(elapsedTimeInSeconds);

            int count = m_actorsList.Count;
            for (int i = 0; i < count; i++)
            {
                m_actorsList[i].PropertyChangeEventBehaviorsUpdate(elapsedTimeInSeconds);
            }
        }

        internal void InitAllPropertyChangePreviousValues()
        {
            InitPropertyChangePreviousValues();
            foreach (Actor actor in m_actorsList)
            {
                actor.InitPropertyChangePreviousValues();
            }
        }

        private void PrepareAllForChangeEvents()
        {
            CheckForChangedProperties();
            InitPropertyChangePreviousValues();
            foreach (Actor actor in m_actorsList)
            {
                actor.CheckForChangedProperties();
                actor.InitPropertyChangePreviousValues();
            }
        }

        private void CleanupActors()
        {
            foreach (Actor actor in m_actorsList)
            {
                actor.PushingActor = null;
            }

            foreach (Actor actor in m_actorRemoveQueue)
            {
                m_actorsDictionary.Remove(actor.Name);
                m_actorsList.Remove(actor);

                actor.RemoveBehaviors();
                actor.DecrementCount();
            }
            m_actorRemoveQueue.Clear();
        }

        public Scene(Game game, Dictionary<string, Data.ActorInfo> actors, Data.SceneInfo sceneInfo) : base(game, sceneInfo.Name)
        {
            m_game = game;

            if (sceneInfo.Audio != null && sceneInfo.Audio.Url != null)
            {
                MusicUrl = sceneInfo.Audio.Url;
            }

            switch (sceneInfo.Type)
            {
                case "splash" :
                    Type = SceneType.INTRO;
                    break;
                case null :
                case "undefined" :
                    Type = SceneType.NORMAL;
                    break;
                case "won" :
                    Type = SceneType.WON;
                    break;
                case "lost" :
                    Type = SceneType.LOST;
                    break;
                case "instructions" :
                    Type = SceneType.HOWTOPLAY;
                    break;
                default :
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to construct a scene of type '" + sceneInfo.Type + "'.");
            }

            m_sceneCanvas = new Canvas();

            m_sceneCanvas.Background = (Type == SceneType.HOWTOPLAY ? 
                new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.White):
                new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Transparent));

            if(Type == SceneType.HOWTOPLAY)
            {
                m_sceneCanvas.Width = sceneInfo.Width;
                m_sceneCanvas.Height = sceneInfo.Height;
            }
            if (sceneInfo.Appearance != null)
            {
                string key = Utils.KeyFromResourceAttribute(sceneInfo.Appearance);
                string xaml = game.GameResources.ResourceDictionary[key];

                // Grab any scene xaml and add things to the scene canvas with 
                // hit testing turned off and textblock fongts fixed
                object drawings = Utils.CreateFromXaml(xaml);

                if (drawings.GetType() == typeof(Canvas))
                {
                    Canvas drawingsCanvas = (Canvas)drawings;

                    if (drawingsCanvas.Background != null &&
                        drawingsCanvas.Background.GetType() == typeof(System.Windows.Media.SolidColorBrush))
                    {
                        m_sceneCanvas.Background = drawingsCanvas.Background;
                    }

                    while (drawingsCanvas.Children.Count > 0)
                    {
                        UIElement el = drawingsCanvas.Children[0];
                        drawingsCanvas.Children.Remove(el);
                        m_sceneCanvas.Children.Add(el);

                        el.IsHitTestVisible = false; // for perf
                    }
                }
            }

            IsActor = false;

            m_actorData = actors;

            m_sceneInfo = sceneInfo;

            foreach (KeyValuePair<string, Data.PropertyInfo> kvp in sceneInfo.properties)
            {
                try
                {
                    SetValue(kvp.Key, kvp.Value.Value);
                }
                catch (Exception e)
                {
                    System.Text.StringBuilder errorMessage = new System.Text.StringBuilder(
                        "Cannot set '" + kvp.Key + "' to '" + kvp.Value.Value + "' on scene '" + Name +
                        "'.\n");
                    errorMessage.Append("Message: " + e.Message + "\n");
                    errorMessage.Append("Exception: " + e.ToString() + "\n");
                    Console.ReportError(errorMessage.ToString());
                }
            }

            // set these after the properties are set since the properties will default width and height to 0
            Width = sceneInfo.Width;
            Height = sceneInfo.Height;
            m_sceneCanvas.Width = sceneInfo.Width;
            m_sceneCanvas.Height = sceneInfo.Height;
            if (sceneInfo.UsingViewport)
            {
                ViewportWidth = sceneInfo.ViewportWidth;
                ViewportHeight = sceneInfo.ViewportHeight;
                ViewportX = sceneInfo.ViewportX;
                ViewportY = sceneInfo.ViewportY;
            }
            else
            {
                ViewportWidth = sceneInfo.Width;
                ViewportHeight = sceneInfo.Height;
            }

            if (m_sceneInfo.ActorInstances != null)
            {
                foreach (KeyValuePair<string, Data.ActorInstanceInfo> kvp in m_sceneInfo.ActorInstances)
                {
                    Actor actor = new Actor(m_game, this, m_actorData[kvp.Value.ActorName], kvp.Value);
                    m_sceneCanvas.Children.Add(actor.GetCanvasRoot());
                    m_actorsDictionary.Add(kvp.Key, actor);
                    m_actorsList.Add(actor);
                }
            }

            if (sceneInfo.Background != null && sceneInfo.Background.ActorReference != null)
            {
                if (actors.ContainsKey(sceneInfo.Background.ActorReference.Name))
                {
                    AddBackground(actors[sceneInfo.Background.ActorReference.Name], game.GameResources, sceneInfo.Background.Transform);
                }
                else
                {
                    Console.ReportError("Could not add background '" +
                        sceneInfo.Background.ActorReference.Name +
                        "' because no actor of that name exists.");
                }
            }

            if (sceneInfo == game.FirstSceneInfo)
            {
                m_sceneCanvas.Children.Add(new MandatorySplash(Width, Height, ViewportWidth, ViewportHeight));
            }

            InitBehaviors(this, sceneInfo.Behaviors);
            foreach (Actor actor in m_actorsList)
            {
                actor.InitActorBehaviors();
            }
        }

        private void AddBackground(Data.ActorInfo backgroundActorInfo, Data.Resources resources, string transform)
        {
            string xaml = resources.ResourceDictionary[Utils.KeyFromResourceAttribute(backgroundActorInfo.States.getIndexedValue(0).Appearance)];

            if (m_backgroundCanvas != null)
            {
                m_sceneCanvas.Children.Remove(m_backgroundCanvas);
            }

            switch (transform)
            {
                case "Scale":
                    m_backgroundCanvas = Utils.CreateFromXaml(xaml);

                    ScaleTransform scaleTransform = new ScaleTransform();

                    scaleTransform.ScaleX = Width / m_backgroundCanvas.Width;
                    scaleTransform.ScaleY = Height / m_backgroundCanvas.Height;
                    m_backgroundCanvas.RenderTransform = scaleTransform;




                    break;
                case "Tile":
                    Canvas tileSizeChecker = Utils.CreateFromXaml(xaml);
                    int numRows = (int)(Height / tileSizeChecker.Height);
                    if ((Height % tileSizeChecker.Height) != 0)
                    {
                        numRows++;
                    }
                    int numColumns = (int)(Width / tileSizeChecker.Width);
                    if ((Width % tileSizeChecker.Width) != 0)
                    {
                        numColumns++;
                    }

                    m_backgroundCanvas = new Canvas();
                    for (int i = 0; i < numColumns; i++)
                    {
                        for (int j = 0; j < numRows; j++)
                        {
                            Canvas tile = Utils.CreateFromXaml(xaml);
                            Canvas.SetTop(tile, tile.Height * j);
                            Canvas.SetLeft(tile, tile.Width * i);
                            m_backgroundCanvas.Children.Add(tile);
                        }
                    }
                    break;
                default:
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "Don't know how to add a background with scale type '" + transform + "'.");
            }

            Canvas.SetZIndex(m_backgroundCanvas, -9999);
            m_backgroundCanvas.IsHitTestVisible = false;
            m_sceneCanvas.Children.Add(m_backgroundCanvas);
        }

        public Actor AddActor(Actor parent, string actorName)
        {
            Data.ActorInfo actorInfo;
            if (m_actorData.TryGetValue(actorName, out actorInfo))
            {
                double x = parent.GetCenterX() - actorInfo.States.getIndexedValue(0).Width / 2;
                double y = parent.GetCenterY() - actorInfo.States.getIndexedValue(0).Height / 2;
                double rotation = parent.Rotation;
                return AddActor(x, y, parent, actorName);
            }
            else
            {
                return null;
            }
        }

        public Actor AddActor(double x, double y, Actor parent, string actorName)
        {
            double rotation = parent.Rotation;
            return AddActor(x, y, rotation, 1.0, 1.0, actorName, null, parent);
        }

        public Actor AddActor(double x, double y, string actorName)
        {
            return AddActor(x, y, actorName, null);
        }
        public Actor AddActor(double x, double y, string actorName, Data.EffectInfo effect)
        {
            return AddActor(x, y, 0.0, 1.0, 1.0, actorName, effect);
        }

        private Actor AddActor(double x, double y, double rotation, double scaleX, double scaleY, string actorName, Data.EffectInfo effect, Actor parent)
        {

            Console.Write("MSGG002: Spawning a new instance of " + actorName);
            Data.ActorInfo actorInfo;
            if (m_actorData.TryGetValue(actorName, out actorInfo))
            {
                string name = "dynamically-created " + actorInfo.Name + " " + DateTime.Now.Ticks + m_actorsList.Count;
                Actor actor = new Actor(m_game, this, actorInfo, name, x, y, rotation, scaleX, scaleY, effect);

                actor.Parent = parent;

                if (parent != null && CollisionMath.OverlapActorActor(actor, parent) != null)
                {
                    actor.InitialChildOverlap = true;
                }

                m_actorsList.Add(actor);
                m_actorsDictionary.Add(name, actor);

                if (parent != null)
                {
                    Canvas.SetZIndex(actor.GetCanvasRoot(), Canvas.GetZIndex(parent.GetCanvasRoot()));
                    // This is what the old javascript engine does.  We could insert it right before the parent using index of instead,
                    // but for consistencies sake, I'm inserting it at 0.
                    m_sceneCanvas.Children.Insert(0, actor.GetCanvasRoot());
                }
                else
                {
                    m_sceneCanvas.Children.Add(actor.GetCanvasRoot());
                }

                return actor;
            }
            else
            {
                return null;
            }
        }

        public Actor AddActor(double x, double y, double rotation, double scaleX, double scaleY, string actorName, Data.EffectInfo effect)
        {
            return AddActor(x, y, rotation, scaleX, scaleY, actorName, effect, null);
        }

        public List<Actor> GetActors()
        {
            return m_actorsList;
        }

        public class JSCompatList<T> : List<T>
        {
            public int length { get { return Count; } }
            public void push(T toAdd) { Add(toAdd); }
            public T pop()
            {
                T last = this[Count - 1];
                RemoveAt(Count - 1);
                return last;
            }
            public T shift()
            {
                T first = this[0];
                RemoveAt(0);
                return first;
            }
            public void reverse(){ Reverse(); }
            public void sort() { Sort(); }
            
        }

        /// <summary>
        /// Call this method to obtain a list of all active ActorInstances of an Actor in the current scene.
        /// </summary>
        /// <param name="actorName">The name of the Actor whose instances you would like to retrieve.</param>
        /// <returns>List of ActorInstances of the Actor matching the name parameter</returns>
        public JSCompatList<Actor> GetActors(string actorName)
        {
            List<Actor> result = m_actorsList.Where(a => a.ActorName == actorName).ToList();
            JSCompatList<Actor> jslist = new JSCompatList<Actor>();
            foreach (Actor actor in result)
            {
                jslist.Add(actor);
            }
            return jslist;
        }

        /// <summary>
        /// Use this method to obtain a particular ActorInstance if you know its name.
        /// </summary>
        /// <param name="instanceName">The name of the ActorInstance you would like to retrieve.</param>
        /// <returns>The ActorInstance matching the instanceName parameter or null if none exists.</returns>
        public Actor GetActor(string instanceName)
        {
            Actor retVal;
            m_actorsDictionary.TryGetValue(instanceName, out retVal);
            return retVal;
        }

        public override Canvas GetCanvasRoot()
        {
            return m_sceneCanvas;
        }

        public override void SetValue(String propertyName, Object value)
        {
            try
            {
                switch (propertyName)
                {
                    case "ViewportX":
                    case "X":
                        X = Utils.ValidDoubleFromObject(value);
                        break;
                    case "ViewportY":
                    case "Y":
                        Y = Utils.ValidDoubleFromObject(value);
                        break;
                    case "XVelocity":
                        XVelocity = Utils.ValidDoubleFromObject(value);
                        break;
                    case "YVelocity":
                        YVelocity = Utils.ValidDoubleFromObject(value);
                        break;
                    case "XAcceleration":
                        XAcceleration = Utils.ValidDoubleFromObject(value);
                        break;
                    case "YAcceleration":
                        YAcceleration = Utils.ValidDoubleFromObject(value);
                        break;
                    case "XDrag":
                        XDrag = Utils.ValidDoubleFromObject(value);
                        break;
                    case "YDrag":
                        YDrag = Utils.ValidDoubleFromObject(value);
                        break;
                    case "Width":
                        Width = Utils.ValidDoubleFromObject(value);
                        break;
                    case "Height":
                        Height = Utils.ValidDoubleFromObject(value);
                        break;
                    case "ViewportWidth":
                        ViewportWidth = Utils.ValidDoubleFromObject(value);
                        break;
                    case "ViewportHeight":
                        ViewportHeight = Utils.ValidDoubleFromObject(value);
                        break;
                    default:
                        base.SetValue(propertyName, value);
                        break;
                }
            }
            catch (Exception e)
            {
                Console.ReportError(
                    "Error setting property '" + propertyName + "' to value '" + value.ToString() + " on scene '" + Name + "'.\n" +
                    "Message: " + e.Message + "\n" +
                    "Exception: " + e.ToString() + "\n");
            }
        }

        public override Object GetValue(String propertyName)
        {
            switch (propertyName)
            {
                case "X":
                    return ViewportX;
                case "Y":
                    return ViewportY;
                case "XVelocity":
                    return XVelocity;
                case "YVelocity":
                    return YVelocity;
                case "XAcceleration":
                    return XAcceleration;
                case "YAcceleration":
                    return YAcceleration;
                case "XDrag":
                    return XDrag;
                case "YDrag":
                    return YDrag;
                case "Width":
                    return Width;
                case "Height":
                    return Height;
                case "ViewportWidth":
                    return ViewportWidth;
                case "ViewportHeight":
                    return ViewportHeight;
                case "Name":
                    return Name;
                default:
                    return base.GetValue(propertyName);
            }
        }


        private List<CollisionClassWithListener> GetCollisionListenersForActor(Actor actor)
        {
            List<CollisionClassWithListener> actorsListeners = new List<CollisionClassWithListener>();

            if (actor.IsSolid)
            {
                actorsListeners.AddRange(m_solidListenersOtherClass);
            }
            else
            {
                actorsListeners.AddRange(m_nonSolidListenersOtherClass);
            }

            List<CollisionClassWithListener> addThese;
            if (m_actorInstanceListenersOtherClass.TryGetValue(actor.Name, out addThese))
            {
                actorsListeners.AddRange(addThese);
            }

            if (m_actorListenersOtherClass.TryGetValue(actor.ActorName, out addThese))
            {
                actorsListeners.AddRange(addThese);
            }



            return actorsListeners;
        }

        private class CollisionListeners
        {
            public List<CollisionListener> OnCollideListeners = new List<Events.CollisionListener>();
            public List<CollisionListener> CollideListeners = new List<Events.CollisionListener>();
        }


        private List<CollisionListener> OrderAndUniquifyCollisionListeners(List<CollisionListener> listeners)
        {
            Dictionary<BehaviorOwner, List<CollisionListener>> collisionsForOwner = new Dictionary<BehaviorOwner, List<CollisionListener>>();
            foreach (CollisionListener listener in listeners)
            {
                if (collisionsForOwner.ContainsKey(listener.Owner) == false)
                {
                    collisionsForOwner[listener.Owner] = new List<CollisionListener>();
                }
                collisionsForOwner[listener.Owner].Add(listener); 
            }
            List<CollisionListener> orderedList = new List<CollisionListener>();

            if (collisionsForOwner.ContainsKey(this))
            {
                orderedList.AddRange(this.OrderCollisionBehaviors(collisionsForOwner[this]));
            }
            foreach (Actor actor in m_actorsList)
            {
                if (collisionsForOwner.ContainsKey(actor))
                {
                    orderedList.AddRange(actor.OrderCollisionBehaviors(collisionsForOwner[actor]));
                }
            }

            return orderedList;
        }

        private CollisionListeners GetCollisionListeners(List<CollisionClassWithListener> actorsListeners, Actor otherActor)
        {
            CollisionListeners listeners = new CollisionListeners();
            bool addListener;
            foreach (CollisionClassWithListener actorListener in actorsListeners)
            {
                addListener = false;
                switch (actorListener.Type)
                {
                    case CollisionClass.SubType.ACTOR :
                        if (actorListener.ActorOrInstance == otherActor.ActorName)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.ACTORINSTANCE :
                        if (actorListener.ActorOrInstance == otherActor.Name)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.NONSOLID :
                        if (!otherActor.IsSolid)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.SOLID :
                        if (otherActor.IsSolid)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.ALL:
                        addListener = true;
                        break;
                    default :
                        throw new NotImplementedException(
                            Utils.ErrorMessagePrefix + 
                            "GetCollisionListeners doesn't understand collision subtypes of '"  + 
                            actorListener.Type.ToString() + "'.");
                }

                if (addListener)
                {
                    switch (actorListener.Owner.CollisionEvent)
                    {
                        case Events.CollisionListener.CollisionEventType.ONCOLLIDE :
                            listeners.OnCollideListeners.Add(actorListener.Owner);
                            break;
                        case Events.CollisionListener.CollisionEventType.COLLIDE :
                            listeners.CollideListeners.Add(actorListener.Owner);
                            break;
                        default :
                            throw new NotImplementedException(
                                Utils.ErrorMessagePrefix + 
                                "We don't know what to do with a collision event type of '" +
                                actorListener.Owner.CollisionEvent.ToString() + 
                                "' in Scene.GetCollisionListeners."
                                );
                    }
                }
            }

            listeners.CollideListeners = OrderAndUniquifyCollisionListeners(listeners.CollideListeners);
            listeners.OnCollideListeners = OrderAndUniquifyCollisionListeners(listeners.OnCollideListeners);
            return listeners;
        }

        private bool PairHasCollisionEndCheckers(List<CollisionClassWithEndChecker> actorsListeners, Actor otherActor)
        {
            foreach (CollisionClassWithEndChecker actorListener in actorsListeners)
            {
                switch (actorListener.Type)
                {
                    case CollisionClass.SubType.ACTOR:
                        if (actorListener.ActorOrInstance == otherActor.ActorName)
                        {
                            return true;
                        }
                        break;
                    case CollisionClass.SubType.ACTORINSTANCE:
                        if (actorListener.ActorOrInstance == otherActor.Name)
                        {
                            return true;
                        }
                        break;
                    case CollisionClass.SubType.NONSOLID:
                        if (!otherActor.IsSolid)
                        {
                            return true;
                        }
                        break;
                    case CollisionClass.SubType.SOLID:
                        if (otherActor.IsSolid)
                        {
                            return true;
                        }
                        break;
                    case CollisionClass.SubType.ALL :
                        return true;
                    default:
                        throw new NotImplementedException(
                            Utils.ErrorMessagePrefix +
                            "GetCollisionListeners doesn't understand collision subtypes of '" +
                            actorListener.Type.ToString() + "'.");
                }
            }

            return false;
        }

        private List<CollisionClassWithEndChecker> GetCollisionEndCheckersForActor(Actor actor)
        {
            List<CollisionClassWithEndChecker> actorsListeners = new List<CollisionClassWithEndChecker>();

            if (actor.IsSolid)
            {
                actorsListeners.AddRange(m_endSolidListenersOtherClass);
            }
            else
            {
                actorsListeners.AddRange(m_endNonSolidListenersOtherClass);
            }

            List<CollisionClassWithEndChecker> addThese;
            if (m_endActorInstanceListenersOtherClass.TryGetValue(actor.Name, out addThese))
            {
                actorsListeners.AddRange(addThese);
            }

            if (m_endActorListenersOtherClass.TryGetValue(actor.ActorName, out addThese))
            {
                actorsListeners.AddRange(addThese);
            }



            return actorsListeners;
        }

        private List<CollisionEndChecker> GetCollisionEndCheckersHelper(List<CollisionClassWithEndChecker> actorsListeners, Actor otherActor)
        {
            List<CollisionEndChecker> listeners = new List<CollisionEndChecker>();

            bool addListener;
            foreach (CollisionClassWithEndChecker actorListener in actorsListeners)
            {
                addListener = false;
                switch (actorListener.Type)
                {
                    case CollisionClass.SubType.ACTOR:
                        if (actorListener.ActorOrInstance == otherActor.ActorName)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.ACTORINSTANCE:
                        if (actorListener.ActorOrInstance == otherActor.Name)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.NONSOLID:
                        if (!otherActor.IsSolid)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.SOLID:
                        if (otherActor.IsSolid)
                        {
                            addListener = true;
                        }
                        break;
                    case CollisionClass.SubType.ALL:
                        addListener = true;
                        break;
                    default:
                        throw new NotImplementedException(
                            Utils.ErrorMessagePrefix +
                            "GetCollisionListeners doesn't understand collision subtypes of '" +
                            actorListener.Type.ToString() + "'.");
                }

                if (addListener)
                {
                    listeners.Add(actorListener.Owner);
                }
            }

            return listeners;
        }

        private List<CollisionEndChecker> GetCollisionEndCheckers(Actor actor, Actor otherActor)
        {
            List<CollisionClassWithEndChecker> endCheckers = GetCollisionEndCheckersForActor(actor);

            return GetCollisionEndCheckersHelper(endCheckers, otherActor);
        }

        // See if this actor has just collided with another actor, and potentially move it.
        private void CheckForCollisions(Actor actor, int index)
        {
            // filter non-solid actors if they have no listeners to collisions containing them
            // if they are "actor".
            // Filter otherActor if it's a non-solid actor and there are no listeners to collisions 
            // between actor and otherActor.
            // Check if there are listeners to this actor
            List<CollisionClassWithListener> actorsListeners = GetCollisionListenersForActor(actor);
            List<CollisionClassWithEndChecker> endListeners = GetCollisionEndCheckersForActor(actor);
            bool currentActorHasListeners = (actorsListeners.Count + endListeners.Count != 0);

            // If there aren't and the actor isn't solid, go ahead and return here since
            // no one needs the messages.
            if (!actor.CurrentState.IsSolid && !currentActorHasListeners)
            {
                return;
            }

            
            CollisionEdges.EdgesType actorEdgesType = actor.CurrentState.Edges.Type;
            var initialScene = m_game.CurrentScene;

            actor.PreviousActorsCollidedWithData = actor.ActorsCollidedWithData;
            actor.ActorsCollidedWithData = new Dictionary<string,Events.CollisionEventArgs>();

            // TODO: We're checking every other actor in the scene right now.  This could be smarter.
            //       We could not only keep track of "close" actors, but also don't bother if nobody is
            //       subscribed to a collision between the relevant actors.
            for (int i = 0; i < m_actorsList.Count; i++)
            {
                // get these each time in case they are updated in a collision resolution
                // (in the old engine this was outside the for loop, but worked ok since 
                //  the helper GetBounds was returning a reference to the active bounds
                CollisionBounds actorBounds = actor.GetBounds();
                Point actorCenter = actor.GetObjectCenter();

                Actor otherActor = m_actorsList[i];
                CollisionListeners listenersForThisPair = GetCollisionListeners(actorsListeners, otherActor);
                bool currentActorPairHasListeners = 
                    (listenersForThisPair.CollideListeners.Count != 0) ||
                    (listenersForThisPair.OnCollideListeners.Count != 0) ||
                    PairHasCollisionEndCheckers(endListeners, otherActor);

                if (!(actor.CurrentState.IsSolid && otherActor.CurrentState.IsSolid) && !currentActorPairHasListeners)
                {
                    continue;
                }

                CollisionBounds otherActorBounds = otherActor.GetBounds();
                Point otherActorCenter = otherActor.GetObjectCenter();
                CollisionEdges.EdgesType otherActorEdgesType = otherActor.CurrentState.Edges.Type;

                if (
                    (i < index && otherActor.MovedThisFrame) ||
                    actor == otherActor ||
                    ((actorEdgesType == CollisionEdges.EdgesType.CIRCLE && otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE) &&
                     (MathUtils.DistanceSquared(actorCenter, otherActorCenter) >
                     (actorBounds.Radius + otherActorBounds.Radius) * (actorBounds.Radius + otherActorBounds.Radius)
                     )
                    )
                   )
                {
                    continue;
                }

                Events.CollisionDirectionArg direction;

                if (actorEdgesType == CollisionEdges.EdgesType.CIRCLE && otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE)
                {
                    direction = CollisionMath.OverlapCircleCircle(
                        actorCenter,
                        actorBounds.Radius,
                        otherActorCenter,
                        otherActorBounds.Radius);
                }
                else if (actorEdgesType == CollisionEdges.EdgesType.CIRCLE && otherActorEdgesType == CollisionEdges.EdgesType.RECT)
                {
                    direction = CollisionMath.OverlapCircleRect(actorCenter, actorBounds.Radius, otherActorBounds);
                }
                else if (otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE && actorEdgesType == CollisionEdges.EdgesType.RECT)
                {
                    direction = CollisionMath.OverlapRectCircle(actorBounds, otherActorCenter, otherActorBounds.Radius);
                }
                else
                {
                    // Note that the collision detection doesn't factor in rotation/skew
                    direction = CollisionMath.OverlapRectRect(actorBounds, otherActorBounds, actor, otherActor);
                }

                if (direction == null) // no overlap
                    continue;

                if (!(actorEdgesType == CollisionEdges.EdgesType.CIRCLE) && 
                    !(otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE))
                {
                    // If overlap occurs with no direction (meaning otherActorBounds is completely inside actorBounds),
                    // use the motion of the actor as the direction.
                    if (direction.Top == 0.0 && direction.Bottom == 0.0 && direction.Left == 0.0 && direction.Right == 0)
                    {
                        double actorMovedX = actor.X - actor.PrevX;
                        double actorMovedWidth = actor.Width - actor.PrevWidth;
                        if (actorMovedX > 0 || actorMovedWidth > 0) // todo: is this the right thing to do with width?
                            direction.Right = actorMovedX;
                        else if (actorMovedX < 0)
                            direction.Left = -actorMovedX;

                        double actorMovedY = actor.Y - actor.PrevY;
                        double actorMovedHeight = actor.Height - actor.PrevHeight;
                        if (actorMovedY > 0 || actorMovedHeight > 0)
                            direction.Bottom = actorMovedY;
                        else if (actorMovedY < 0)
                            direction.Top = -actorMovedY;

                    }
                }

                Events.CollisionDirectionArg reverseDirection = new Events.CollisionDirectionArg();

                reverseDirection.Right = direction.Left;
                reverseDirection.Left = direction.Right;
                reverseDirection.Bottom = direction.Top;
                reverseDirection.Top = direction.Bottom;

                switch (direction.FilterDirection)
                {
                    case Events.CollisionDirectionArg.FilterDirections.FILTER_TOP:
                        reverseDirection.FilterDirection = Events.CollisionDirectionArg.FilterDirections.FILTER_BOTTOM;
                        break;
                    case Events.CollisionDirectionArg.FilterDirections.FILTER_BOTTOM:
                        reverseDirection.FilterDirection = Events.CollisionDirectionArg.FilterDirections.FILTER_TOP;
                        break;
                    case Events.CollisionDirectionArg.FilterDirections.FILTER_LEFT:
                        reverseDirection.FilterDirection = Events.CollisionDirectionArg.FilterDirections.FILTER_RIGHT;
                        break;
                    case Events.CollisionDirectionArg.FilterDirections.FILTER_RIGHT:
                        reverseDirection.FilterDirection = Events.CollisionDirectionArg.FilterDirections.FILTER_LEFT;
                        break;
                    case Events.CollisionDirectionArg.FilterDirections.FILTER_NONE:
                        reverseDirection.FilterDirection = Events.CollisionDirectionArg.FilterDirections.FILTER_NONE;
                        break;
                }

                if ((actorEdgesType == CollisionEdges.EdgesType.CIRCLE) && 
                    (otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE))
                {
                    reverseDirection.X = -direction.X;
                    reverseDirection.Y = -direction.Y;
                }


                
                if (!otherActor.ActorsCollidedWith.ContainsKey(actor.Name)) // Don't raise the event if the other actor already did
                {
                    Events.CollisionEventArgs collisionData = 
                        new Events.CollisionEventArgs(actor, otherActor, direction, reverseDirection);
                    
                    if (currentActorPairHasListeners) 
                    {
                        foreach (Events.CollisionListener listener in listenersForThisPair.CollideListeners)
                        {
                            listener.Raise(actor, collisionData);
                        }
                    }
                    if (m_game.CurrentScene != initialScene) // TODO: this probably is no longer necessary
                    {
                        return;
                    }
                    // Store the collision data for use when neither are moving.
                    // We store it only for one of the two actors so we can check for it and only raise it for one of the two actors.
                    actor.ActorsCollidedWithData[otherActor.Name] = collisionData;
                }

                if (!actor.PreviousActorsCollidedWith.ContainsKey(otherActor.Name))
                {
                    // Raise CollisionStart, except for initial parent/child overlap cases
                    if ((actor.Parent == otherActor && actor.InitialChildOverlap) ||
                        (otherActor.Parent == actor && otherActor.InitialChildOverlap))
                    {
                        // Do nothing.  I was going to clear the initialChildOverlap flag here, but this doesn't work
                        // since we try to raise CollisionStart twice when two moving actors collide.
                        // So the clearing is being done in raiseEvent, along with the clearing of previousActorsCollidedWith.
                
                    }
                    else
                    {
                        if (!otherActor.ActorsCollidedWith.ContainsKey(actor.Name)) // Don't raise the event if the other actor already did
                        {
                            foreach (Events.CollisionListener listener in listenersForThisPair.OnCollideListeners)
                            {
                                listener.Raise(
                                    actor, new Events.CollisionEventArgs(actor, otherActor, direction, reverseDirection));
                            }
                            if (m_game.CurrentScene != initialScene) // TODO: this is probably no longer necessary
                            {
                                return;
                            }
                        }
                    }
                }
                actor.ActorsCollidedWith[otherActor.Name] = otherActor;
                otherActor.ActorsCollidedWith[actor.Name] = actor;
          

                // If the two actors are still there and overlapping and they are both solid actors,
                // this overlap must be undone
                // TODO: We really should separate out the detection from the resolution so we're not
                // doing anything except detecting collisions above and detecting them again here then resolving.
                if (!actor.Removed && !otherActor.Removed &&
                    actor.CurrentState.IsSolid && otherActor.CurrentState.IsSolid)
                {
                    if (actorEdgesType == CollisionEdges.EdgesType.CIRCLE && otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE)
                    {
                        direction = CollisionMath.OverlapCircleCircle(
                            actorCenter,
                            actorBounds.Radius,
                            otherActorCenter,
                            otherActorBounds.Radius);
                    }
                    else if (actorEdgesType == CollisionEdges.EdgesType.CIRCLE && otherActorEdgesType == CollisionEdges.EdgesType.RECT)
                    {
                        direction = CollisionMath.OverlapCircleRect(actorCenter, actorBounds.Radius, otherActorBounds);
                    }
                    else if (otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE && actorEdgesType == CollisionEdges.EdgesType.RECT)
                    {
                        direction = CollisionMath.OverlapRectCircle(actorBounds, otherActorCenter, otherActorBounds.Radius);
                    }
                    else
                    {
                        // Note that the collisiondetection doesn't factor in rotation/skew
                        direction = CollisionMath.OverlapRectRect(actorBounds, otherActorBounds, actor, otherActor);
                    }

                    if (direction == null) // No overlap.  The user corrected the situation already!
                        continue;

                    Point backupOffset = new Point();

                    if ((actorEdgesType == CollisionEdges.EdgesType.CIRCLE && otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE) ||
                        (actorEdgesType == CollisionEdges.EdgesType.CIRCLE && otherActorEdgesType == CollisionEdges.EdgesType.RECT) ||
                        (otherActorEdgesType == CollisionEdges.EdgesType.CIRCLE && actorEdgesType == CollisionEdges.EdgesType.RECT))
                    {
                        backupOffset.X = direction.X;
                        backupOffset.Y = direction.Y;
                    }
                    else
                    {
                        // TODO: Move all the complicated parts into a resolveRectRect function and just do the move here.  
                        // We could let people override the resolves with custom code.

                        // The behaviors (if any) didn't correct the overlap of two solid actors.
                        // Therefore back up the one that collided too far.

                        // If overlap occurs with no direction (meaning otherActorBounds is completely inside actorBounds),
                        // use the motion of the actor as the direction.
                        if (direction.Top == 0.0 && direction.Bottom == 0.0 && direction.Left == 0.0 && direction.Right == 0.0)
                        {
                            double actorMovedX = actor.X - actor.PrevX;
                            double actorMovedWidth = actor.Width - actor.PrevWidth;
                            if (actorMovedX > 0 || actorMovedWidth > 0)
                                direction.Right = actorMovedX;
                            else if (actorMovedX < 0)
                                direction.Left = Math.Abs(actorMovedX);

                            double actorMovedY = actor.Y - actor.PrevY;
                            double actorMovedHeight = actor.Height - actor.PrevHeight;
                            if (actorMovedY > 0 || actorMovedHeight > 0)
                                direction.Bottom = actorMovedY;
                            else if (actorMovedY < 0)
                                direction.Top = Math.Abs(actorMovedY);
                        }

                        // NOTE: The following math is for rectangular edges only.

                        // Determine the backup direction from the edge(s) of otherActor overlapped.
                        // If both top/bottom are overlapped or both left/right, they cancel each other out.                    
                        if (direction.Right > 0 && direction.Left == 0.0)
                            backupOffset.X = direction.Right;
                        else if (direction.Left > 0 && direction.Right == 0.0)
                            backupOffset.X = -direction.Left;

                        if (direction.Bottom > 0 && direction.Top == 0.0)
                            backupOffset.Y = direction.Bottom;
                        else if (direction.Top > 0 && direction.Bottom == 0.0)
                            backupOffset.Y = -direction.Top;

                        // If only one edge overlaps (or 3, which turns into 1 due to cancellation), the backup
                        // direction is good.  If two edges overlap (i.e. a corner), backup the direction with the least
                        // amount of overlap.  If they are equal amounts, leave the backup direction as-is (backing up diagonally).
                        if (backupOffset.X != 0.0 && backupOffset.Y != 0.0)
                        {
                            if (Math.Abs(backupOffset.X) > Math.Abs(backupOffset.Y))
                                backupOffset.X = 0;
                            else if (Math.Abs(backupOffset.Y) > Math.Abs(backupOffset.X))
                                backupOffset.Y = 0;
                        }
                    }

                    if (backupOffset.X != 0.0 || backupOffset.Y != 0.0)
                    {
                        // - If this actor overlaps something that doesn't allow pushing, back it up.
                        // - If it overlaps something that allows pushing but is moving faster
                        //   than it, back it up (so this actor is the one that gets pushed)
                        // - If it overlaps something that allows pushing and is either stationery
                        //   or moving slower, push it!
                        //   TODO: This doesn't work if a 5 pushes a 2 into a 4 and wants both to move (which should work)

                        double actorLengthSq = MathUtils.MagnitudeSquared(new Point(actor.XVelocity, actor.YVelocity));
                        double otherActorLengthSq = MathUtils.MagnitudeSquared(new Point(otherActor.XVelocity, otherActor.YVelocity));

                        //                      var normalizedOffset = Vector2.Normalize(backupOffset);

                        //                      var energyTransfer = 0.99;

                        //TODO: At some point this could consider rotation, too, if we make rotation update the bounds.

                        // push the one with less mass or if they have equal mass, the slower moving one
                        if (actor.Mass > otherActor.Mass ||
                            (actor.Mass == otherActor.Mass &&
                             actor.MovedThisFrame &&
                             actorLengthSq > otherActorLengthSq &&
                             actor.FindInPushedList(otherActor) == false
                            )
                           )
                        {
                            // Push it!
                            double xPrevious = otherActor.PrevX;
                            double yPrevious = otherActor.PrevY;
                            if (backupOffset.X != 0.0)
                            {
                                otherActor.X = otherActorBounds.X - backupOffset.X;
                                // Reset velocity if it's eitehr opposite the actual motion resulting from this push, or if it's in the same direction but of a lesser magnitude
                                if ((-backupOffset.X) * otherActor.XVelocity < -0.0000001 ||
                                    Math.Abs(backupOffset.X / m_game.DeltaTimeInSeconds) > Math.Abs(otherActor.XVelocity))
                                {
                                    // update velocity so it doesn't get out of control
                                    otherActor.XVelocity = (otherActor.X - xPrevious) / m_game.DeltaTimeInSeconds;
                                }
                            }
                            if (backupOffset.Y != 0.0)
                            {
                                otherActor.Y = otherActorBounds.Y - backupOffset.Y;
                                // Reset velocity if it's either opposite the actual motion resulting from this push or if it's in the same direction but of a lesser magnitude
                                if ((-backupOffset.Y) * otherActor.YVelocity < -0.0000001 ||
                                    Math.Abs(backupOffset.Y / m_game.DeltaTimeInSeconds) > Math.Abs(otherActor.YVelocity))
                                {
                                    // update velocity so it doesn't get out of control
                                    otherActor.YVelocity = (otherActor.Y - yPrevious) / m_game.DeltaTimeInSeconds;
                                }
                            }

                            otherActor.PushingActor = actor;
                            m_newMotionsFromCollisions.Add(otherActor);
                        }
                        else
                        {
                            // Back it up (and any other actors that pushed it into this position)!
                            Actor a = actor;

                            while (a != null)
                            {
                                // TODO: what should we do here when scaling?
                                double xPrevious = a.PrevX;
                                double yPrevious = a.PrevY;

                                if (backupOffset.X != 0)
                                {
                                    a.X = a.X + backupOffset.X;
                                    // Reset velocity if it's either opposite the actual motion resulting from this push or if it's in the same direction but of a lesser magnitude
                                    if (backupOffset.X * a.XVelocity < -0.0000001 ||
                                        Math.Abs(backupOffset.X / m_game.DeltaTimeInSeconds) > Math.Abs(a.XVelocity))
                                    {
                                        // update velocity so it doesn't get out of control
                                        a.XVelocity = (a.X - xPrevious) / m_game.DeltaTimeInSeconds;
                                    }
                                }
                                if (backupOffset.Y != 0)
                                {
                                    a.Y = a.Y + backupOffset.Y;
                                    // Reset velocity if it's either opposite the actual motion resulting from this push or if it's in the same direction but of a lesser magnitude
                                    if (backupOffset.Y * a.YVelocity < -0.0000001 ||
                                        Math.Abs(backupOffset.Y / m_game.DeltaTimeInSeconds) > Math.Abs(a.YVelocity))
                                    {
                                        // update velocity so it doesn't get out of control
                                        a.YVelocity = (a.Y - yPrevious) / m_game.DeltaTimeInSeconds;
                                    }
                                }

                                a = a.PushingActor;
                            }
                        }
                    }
                }
            }
        }


        private void ResolveCollisions()
        {
            m_newMotionsFromCollisions.Clear();

            // During collision resolution, don't 
            // immediately remove collision listeners
            // since during collision listener execution, 
            // that would screw up the iteration through the 
            // collision listener lists.  
            m_queueCollisionListenerRemoves = true;
            for (int i = 0; i < m_actorsList.Count; i++)
            {
                if (m_actorsList[i].MovedThisFrame)
                {
                    CheckForCollisions(m_actorsList[i], i);
                }
            }
            if (m_newMotionsFromCollisions.Count > 0)
            {
                int tries = 0;
                int allowedTries = m_actorsList.Count * 2; // safety net to prevent infinite loop
                while (m_newMotionsFromCollisions.Count > 0 && tries < allowedTries)
                {
                    // "Dequeue the first actor in the list
                    Actor actor = m_newMotionsFromCollisions[0];
                    m_newMotionsFromCollisions.RemoveAt(0);

                    CheckForCollisions(actor, 0);
                    tries++;
                    //System.Diagnostics.Debug.Assert(tries < allowedTries - 1);
                }
            }

            // copy previous collision state for non-moved actors
            for (var i = 0; i < m_actorsList.Count; i++)
            {
                Actor actor = m_actorsList[i];
                if (!actor.MovedThisFrame)
                {
                    foreach (KeyValuePair<string, Actor> kvp in actor.PreviousActorsCollidedWith)
                    {
                        string otherActorInstanceName = kvp.Key;
                        Actor otherActor = kvp.Value;
                
                        // If they didn't move this frame either, no collision was raised.  
                        // We store the data on only one actor during checkForCollisions, so 
                        // if that exists on this actor, we can raise the event here.  Otherwise
                        // it will be raised when we get to the other actor.  
                        if (!otherActor.MovedThisFrame && actor.ActorsCollidedWithData.ContainsKey(otherActorInstanceName))
                        {
                            List<CollisionClassWithListener> actorsListeners = GetCollisionListenersForActor(actor);
                            CollisionListeners listenersForThisPair = GetCollisionListeners(actorsListeners, otherActor);

                            foreach (Events.CollisionListener listener in listenersForThisPair.CollideListeners)
                            {
                                listener.Raise(actor, actor.ActorsCollidedWithData[otherActorInstanceName]);
                            }
                            actor.ActorsCollidedWith[otherActorInstanceName] = otherActor;
                            otherActor.ActorsCollidedWith[actor.Name] = actor;
                        }
                    }
                }
            }
    
            // Now that we've done all our collision testing, let's see if we
            // stopped colliding with anything
            for (var i = 0; i < m_actorsList.Count; i++)
            {
                var actor = m_actorsList[i];
        
                foreach (KeyValuePair<string, Actor> kvp in actor.PreviousActorsCollidedWith)
                {
                    string collidedWithInstanceName = kvp.Key;

                    if (actor.Parent != null && // If we are a bullet and have a parent
                        (
                            actor.PreviousActorsCollidedWith.ContainsKey(actor.Parent.Name) && // And last frame we collided with our parent
                            actor.PreviousActorsCollidedWith[actor.Parent.Name] != null
                        ) &&
                        (
                            (!actor.ActorsCollidedWith.ContainsKey(actor.Parent.Name)) || // But this time we didn't
                            actor.ActorsCollidedWith[actor.Parent.Name] == null)
                        )
                    {
                        actor.InitialChildOverlap = false; // That means we're not on our initial collision with our parent
                    }
        
                    // Figure out if we have a collision ending                         // Also check that this is the actor that has the collision data - otherwise the other actor will pick it up.
                    if (!actor.ActorsCollidedWith.ContainsKey(collidedWithInstanceName) && 
                        actor.PreviousActorsCollidedWithData.ContainsKey(collidedWithInstanceName))
                    {   
                        // raise the event
                        List<CollisionEndChecker> endCheckers = GetCollisionEndCheckers(actor, kvp.Value);
                        foreach (CollisionEndChecker endChecker in endCheckers)
                        {
                            endChecker.QueueRaise(actor, actor.PreviousActorsCollidedWithData[collidedWithInstanceName]);
                        }
                    }
                }
            }
    
            for (var i = 0; i < m_actorsList.Count; i++)
            {
                Actor actor = m_actorsList[i];
                // Now that we've raised all our start, colliding and end events, we can set previous to current
                actor.PreviousActorsCollidedWith = actor.ActorsCollidedWith;
                actor.ActorsCollidedWith = new Dictionary<string,Actor>();
            }



            m_queueCollisionListenerRemoves = false;

            // It's now safe to remove them.
            foreach (KeyValuePair<string, CollisionClassWithListener> kvp in m_actorCollisionRemoveQueue)
            {
                m_actorListenersOtherClass[kvp.Key].Remove(kvp.Value);
            }
            m_actorCollisionRemoveQueue.Clear();
            foreach (KeyValuePair<string, CollisionClassWithListener> kvp in m_actorInstanceCollisionRemoveQueue)
            {
                m_actorInstanceListenersOtherClass[kvp.Key].Remove(kvp.Value);
            }
            m_actorInstanceCollisionRemoveQueue.Clear();
            foreach (CollisionClassWithListener otherClass in m_solidCollisionRemoveQueue)
            {
                m_solidListenersOtherClass.Remove(otherClass);
            }
            m_solidCollisionRemoveQueue.Clear();
            foreach (CollisionClassWithListener otherClass in m_nonSolidCollisionRemoveQueue)
            {
                m_nonSolidListenersOtherClass.Remove(otherClass);
            }
            m_nonSolidCollisionRemoveQueue.Clear();
        }

        internal void RemoveVisual(Canvas actorCanvas)
        {
            m_sceneCanvas.Children.Remove(actorCanvas);
        }

        internal void QueueForRemoval(Actor actor, bool removeVisual)
        {
            m_actorRemoveQueue.Add(actor);
            if (removeVisual)
            {
                RemoveVisual(actor.GetCanvasRoot());
                // TODO: potentially move this to the actual removal
            }
        } // End Method

        internal void DoPause()
        {
            ClearMouseEventsForPause();
            foreach (Actor actor in m_actorsList)
            {
                actor.ClearMouseEventsForPause();
            }
        }
    } // End Scene Class
} // End Namespace
